﻿using System;
using System.Threading.Tasks;

namespace N3P.WCF.Proxyless.FullFramework45
{
    /// <summary>
    /// Helpers for working with continuation pattern methods as <see cref="Task"/> objects
    /// </summary>
    internal static class Continuation
    {
        /// <summary>
        /// Expresses a continuation method as a <see cref="Task{TResult}"/>
        /// </summary>
        /// <typeparam name="TResult">The type of the successful completion of the caller</typeparam>
        /// <param name="perform">The method that will call the continuation method upon completion</param>
        /// <returns>A <see cref="Task{TResult}"/> containing the successful result (or exception) provided by the caller</returns>
        public static Task<TResult> AsTask<TResult>(Action<Action<TResult, Exception>> perform)
        {
            var completionSource = new TaskCompletionSource<TResult>();

            try
            {
                perform((result, ex) =>
                {
                    if (ReferenceEquals(ex, null))
                    {
                        completionSource.SetResult(result);
                    }
                    else
                    {
                        completionSource.SetException(ex);
                    }
                });
            }
            catch(Exception ex)
            {
                completionSource.SetException(ex);
            }

            return completionSource.Task;
        }

        /// <summary>
        /// Expresses a continuation method as a <see cref="Task"/>
        /// </summary>
        /// <param name="perform">The method that will call the continuation method upon completion</param>
        /// <returns>A <see cref="Task"/> that will complete when the operation has finished</returns>
        public static Task AsTask(Action<Action<Exception>> perform)
        {
            var completionSource = new TaskCompletionSource<object>();

            perform(ex =>
            {
                if (ReferenceEquals(ex, null))
                {
                    completionSource.SetResult(null);
                }
                else
                {
                    completionSource.SetException(ex);
                }
            });

            return completionSource.Task;
        }

        /// <summary>
        /// Expresses a continuation method as a <see cref="Task{TResult}"/>
        /// </summary>
        /// <param name="perform">The method that will call the continuation method upon completion</param>        
        /// <returns>A <see cref="Task{TResult}"/> containing the successful result (or exception) provided by the caller</returns>
        public static Task<TResult> AsTask<TResult>(Action<Action<TResult>> perform)
        {
            var completionSource = new TaskCompletionSource<TResult>();

            try
            {
                perform(result =>
                {
                    try
                    {
                        completionSource.SetResult(result);
                    }
                    catch (Exception ex)
                    {
                        completionSource.SetException(ex);
                    }
                });
            }
            catch(Exception e)
            {                   
                completionSource.SetException(e);
            }

            return completionSource.Task;
        }
    }
}
